module spectrum_fft use iso_fortran_env use fftpack, only : dffti, dfftf, dfftb use spectrum_windows use spectrum_routines implicit none private public :: stft public :: rfft public :: irfft public :: stft_result type stft_result !! A container for STFT output. complex(real64), allocatable, dimension(:,:) :: stft !! An M-by-N matrix containing the M-element complex-valued !! transforms for each of the N time points studied. M is the !! size of the positive half of the transform, and N is the total !! number of transformed segments. integer(int32), allocatable, dimension(:) :: offsets !! The starting indices of each window segment. end type contains ! ------------------------------------------------------------------------------ pure function rfft(x, n) result(rst) !! Computes the Fourier transform of a real-valued data set. Only the !! positive half of the transform is returned. real(real64), intent(in), dimension(:) :: x !! The real-valued array to transform. integer(int32), intent(in), optional :: n !! An optional input that can be used to specify the length of the !! transform. If less than the length of x, x is truncated; however, !! if greater than the length of x, x is padded with zeros. complex(real64), allocatable, dimension(:) :: rst !! The complex-valued result of the transform. ! Local Variables integer(int32) :: i, m, nx, nxfrm, lensav real(real64), allocatable, dimension(:) :: wsave, rrst ! Initialization nx = size(x) if (present(n)) then nxfrm = n if (nxfrm <= nx) then rrst = x(1:nxfrm) else rrst = [x, (0.0d0, i = 1, nxfrm - nx)] end if else nxfrm = nx rrst = x end if lensav = 2 * nxfrm + 15 allocate(wsave(lensav)) call dffti(nxfrm, wsave) ! Process call dfftf(nxfrm, rrst, wsave) m = compute_transform_length(nxfrm) allocate(rst(m)) rst = unpack_real_transform(rrst) end function ! ------------------------------------------------------------------------------ pure function irfft(x, n) result(rst) !! Computes the inverse Fourier transform for a real-valued data set. complex(real64), intent(in), dimension(:) :: x !! The positive half of the transform. integer(int32), intent(in), optional :: n !! An optional input that can be used to specify the length of the !! transform. If less than the length of x, x is truncated; however, !! if greater than the length of x, x is padded with zeros. real(real64), allocatable, dimension(:) :: rst !! The real-valued result of the inverse transform. ! Local Variables integer(int32) :: i, m, nx, nxfrm, lensav, mn real(real64), allocatable, dimension(:) :: wsave ! Initialization nx = size(x) if (mod(nx, 2) == 0) then m = 2 * nx - 1 else m = 2 * (nx - 1) end if allocate(rst(m), source = 0.0d0) if (present(n)) then nxfrm = n else nxfrm = nx end if mn = min((m + 1) / 2, nx) rst(1) = real(x(1), real64) do i = 2, mn rst(2*i-2) = real(x(i), real64) rst(2*i-1) = aimag(x(i)) end do lensav = 2 * nxfrm + 15 allocate(wsave(lensav)) ! Process call dfftb(nxfrm, rst, wsave) end function ! ------------------------------------------------------------------------------ pure function stft(win, x) result(rst) !! Computes the short time Fourier transform of a signal. !! !! See Also !! !! - [Wikipedia - Short Time Fourier Transform](https://en.wikipedia.org/wiki/Short-time_Fourier_transform) class(window), intent(in) :: win !! The window to apply. real(real64), intent(in) :: x(:) !! The signal to analyze. The signal must be longer than the size of !! the window. type(stft_result) :: rst !! A container filled with the transform results. ! Local Variables integer(int32) :: i, j, k, m, nx, nxfrm, nk, lwork, i1, nend real(real64) :: del, sumw, w, fac, scale real(real64), allocatable, dimension(:) :: work, buffer ! Initialization nx = size(x) m = win%size ! # of rows in the output matrix (transform length) nxfrm = compute_transform_length(m) lwork = 2 * m + 15 nk = compute_overlap_segment_count(nx, m) if (nk > 1) then del = (nx - m) / (nk - 1.0d0) else del = 0.0d0 end if if (mod(m, 2) == 0) then nend = nxfrm - 1 scale = 2.0d0 / m else nend = nxfrm scale = 2.0d0 / (m - 1.0d0) end if ! Input Checking if (m > nx) return ! Memory Allocation allocate(work(lwork), buffer(m)) allocate(rst%stft(nxfrm, nk)) allocate(rst%offsets(nk), source = 0) ! Initialize the the transform. As all transforms are the ! same length, this array can be shared. call dffti(m, work) ! Compute each transform do i = 1, nk ! Compute the offset i1 = int((i - 1) * del + 0.5d0, int32) ! if (present(offsets)) offsets(i) = i1 + 1 rst%offsets(i) = i1 + 1 ! Apply the window sumw = 0.0d0 j = 0 do k = 1, m w = win%evaluate(j) j = j + 1 sumw = sumw + w buffer(k) = w * x(k + i1) end do fac = m / sumw ! Compute the transform call dfftf(m, buffer, work) ! Scale the transform rst%stft(1,i) = fac * scale * cmplx(buffer(1), 0.0d0, real64) do k = 2, nend rst%stft(k,i) = fac * scale * cmplx(buffer(2*k-1), buffer(2*k), real64) end do if (nend /= nxfrm) then rst%stft(nxfrm,i) = fac * scale * cmplx(buffer(m), 0.0d0, real64) end if end do end function ! ------------------------------------------------------------------------------ end module